package com.springboot.learn.retry;

import com.springboot.learn.retry.model.MyRetryContext;
import com.springboot.learn.retry.model.RetryTargetInfo;
import com.springboot.learn.retry.repository.RetryTaskRepository;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.UUID;

/**
 * @author BG343674
 * created by BG343674 on 2019/9/19
 */
@Slf4j
@Aspect
@Component
public class RetryAspect {

    @Autowired
    private RetryTaskRepository retryTaskRepository;

    /**
     * 扫描所有带retry注解的方法
     * 抛出异常后进行重试
     *
     * @param point
     * @param annotation
     * @return
     */
    @AfterThrowing(value = "execution(public * com.springboot.learn.retry..*.*(..)) && @annotation(annotation)", throwing = "h")
    public void after(JoinPoint point, Retry annotation, Throwable h) throws Throwable {
        MyRetryContext retryContext = getTaskContext(point);
        String sign = sign(retryContext);
        if (!retryTaskRepository.taskExist(sign)) {
            // 不存在 新增
            retryContext.setException(h);
            retryTaskRepository.save(retryContext);
        }
        throw h;
    }

    /**
     * 生成taskContext
     *
     * @return
     */
    private MyRetryContext getTaskContext(JoinPoint point) throws NoSuchMethodException {
        final Method method = getCurrentMethod(point);
        MyRetryContext retryContext = new MyRetryContext();
        // 获取uuid
        retryContext.setUuid(UUID.randomUUID().toString());
        RetryTargetInfo retryTargetInfo = new RetryTargetInfo();
        retryTargetInfo.setClazz(method.getDeclaringClass());
        retryTargetInfo.setMethod(method.getName());
        retryTargetInfo.setArgsType(method.getParameterTypes());
        retryTargetInfo.setArgs(point.getArgs());
        retryContext.setRetryTargetInfo(retryTargetInfo);
        retryContext.setTime(System.currentTimeMillis());
        retryContext.setStatus(1);
        return retryContext;
    }

    /**
     * 获取当前方法信息
     *
     * @param point 切点
     * @return 方法
     */
    private Method getCurrentMethod(JoinPoint point) throws NoSuchMethodException {
        Signature sig = point.getSignature();
        MethodSignature msig = (MethodSignature) sig;
        Object target = point.getTarget();
        return target.getClass().getMethod(msig.getName(), msig.getParameterTypes());
    }

    /**
     * 签名
     *
     * @param retryContext
     * @return
     */
    private String sign(MyRetryContext retryContext) {
        RetryTargetInfo.RetryTargetInfoJsonParser jsonParser = RetryTargetInfo.RetryTargetInfoJsonParser.parse(retryContext.getRetryTargetInfo());
        return jsonParser.sign();

    }


}
